package thread;

/**
 * Created with IntelliJ IDEA.
 * Description:
 * User: wx
 * Date: 2024-01-03
 * Time: 18:15
 */

/** 死锁是多线程代码中的一类经典问题
        加锁是能够解决线程安全问题,但是如果加锁的方式不当,就可能产生死锁

        死锁的三种典型场景
        锁 和 死锁 都是并发编程中的重要概念
        1.一个线程 一把锁
        如果是不可重入锁,并且一个线程对这把锁加锁两次 就会出现死锁      相当于钥匙锁屋里了
        2.两个线程,两把锁
        线程1 获取到了 锁A
        线程2 获取到了 锁B
        接下来,1尝试获取 B, 2 尝试获取 A
        就同样出现了死锁                                        相当于车钥匙锁屋里,屋里锁住车钥匙了
        3. N 个线程, M 把锁
        哲学家就餐系统
        例如: 五个人六把筷子 怎么解决吃饭问题
        一旦出现了死锁,线程就"卡住了",无法继续工作
        属于程序中最严重的一类 bug


 解决死锁问题,方案有很多种

   先了解一下,产生死锁的 四个必要条件

 1.互斥使用. 获取锁的过程是互斥的
   一个线程拿到了这把锁,另一个线程也想获取,就需要阻塞等待
 2.不可抢占. 一个线程拿到了锁之后,只能主动解锁,不能让别的线程
   强行把锁抢走
 3.请求保持. 一个线程拿到了锁A,在持有A的前提下,尝试获取B
 4.循环等待/环路等待

解决死锁问题,核心思路,破坏上述的必要条件只要能破坏一个就可以搞定
只要制定一定的规则,就可以有效避免循环等待 */
public class ThreadDemo21 {
    public static void main(String[] args) {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(() -> {
            synchronized (A) {
                //sleep 一下 是给 t2 时间,让 t2 也能拿到 B
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                //尝试获取 B,并没有释放A
                synchronized (B) {
                    System.out.println("t1 拿到了两把锁");
                }
            }
        });
        Thread t2 = new Thread(() ->{

            synchronized (A){
                try {
                    //给 t1 时间,让 t1 能拿到 A
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                //尝试获取 A,并没有释放 B
                synchronized (B) {
                    System.out.println("t2 拿到了两把锁");
                }
            }
        });
        t1.start();
        t2.start();
    }

    public static void main1(String[] args) {
        Object A = new Object();
        Object B = new Object();
        Thread t1 = new Thread(() -> {
           synchronized (A) {
               //sleep 一下 是给 t2 时间,让 t2 也能拿到 B
               try {
                   Thread.sleep(1000);
               } catch (InterruptedException e) {
                   throw new RuntimeException(e);
               }
               //尝试获取 B,并没有释放A
               synchronized (B) {
                   System.out.println("t1 拿到了两把锁");
               }
           }
        });
        Thread t2 = new Thread(() ->{

            synchronized (B){
                try {
                    //给 t1 时间,让 t1 能拿到 A
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
                //尝试获取 A,并没有释放 B
                synchronized (A) {
                    System.out.println("t2 拿到了两把锁");
                }
            }
        });
        t1.start();
        t2.start();
    }
    /**
     * 内存可见性 引起的 线程安全问题
     * */
}
